From ba7ac637bccb06d356fad7a1c1a23cdec8e5372e Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 16 Dec 2016 00:22:23 +0100 Subject: [PATCH] vulkan: Implement staging-buffer image upload This is not enabled by default. Use GSK_RENDERING_MODE=staging-buffer to use the code. --- gsk/gskdebug.c | 1 + gsk/gskdebugprivate.h | 9 +-- gsk/gskvulkanbuffer.c | 26 ++++++-- gsk/gskvulkanbufferprivate.h | 2 + gsk/gskvulkanimage.c | 123 +++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 9 deletions(-) diff --git a/gsk/gskdebug.c b/gsk/gskdebug.c index 02ed8b8de0..7ee7a7b859 100644 --- a/gsk/gskdebug.c +++ b/gsk/gskdebug.c @@ -18,6 +18,7 @@ static const GDebugKey gsk_rendering_keys[] = { { "shaders", GSK_RENDERING_MODE_SHADERS }, { "sync", GSK_RENDERING_MODE_SYNC }, { "staging-image", GSK_RENDERING_MODE_STAGING_IMAGE }, + { "staging-buffer", GSK_RENDERING_MODE_STAGING_BUFFER } }; gboolean diff --git a/gsk/gskdebugprivate.h b/gsk/gskdebugprivate.h index a5d4784fe9..6f5d64ed9a 100644 --- a/gsk/gskdebugprivate.h +++ b/gsk/gskdebugprivate.h @@ -17,10 +17,11 @@ typedef enum { } GskDebugFlags; typedef enum { - GSK_RENDERING_MODE_GEOMETRY = 1 << 0, - GSK_RENDERING_MODE_SHADERS = 1 << 1, - GSK_RENDERING_MODE_SYNC = 1 << 2, - GSK_RENDERING_MODE_STAGING_IMAGE = 1 << 3 + GSK_RENDERING_MODE_GEOMETRY = 1 << 0, + GSK_RENDERING_MODE_SHADERS = 1 << 1, + GSK_RENDERING_MODE_SYNC = 1 << 2, + GSK_RENDERING_MODE_STAGING_IMAGE = 1 << 3, + GSK_RENDERING_MODE_STAGING_BUFFER = 1 << 4 } GskRenderingMode; gboolean gsk_check_debug_flags (GskDebugFlags flags); diff --git a/gsk/gskvulkanbuffer.c b/gsk/gskvulkanbuffer.c index 6694131bd7..3c1d76d37c 100644 --- a/gsk/gskvulkanbuffer.c +++ b/gsk/gskvulkanbuffer.c @@ -15,9 +15,10 @@ struct _GskVulkanBuffer GskVulkanMemory *memory; }; -GskVulkanBuffer * -gsk_vulkan_buffer_new (GdkVulkanContext *context, - gsize size) +static GskVulkanBuffer * +gsk_vulkan_buffer_new_internal (GdkVulkanContext *context, + gsize size, + VkBufferUsageFlags usage) { VkMemoryRequirements requirements; GskVulkanBuffer *self; @@ -32,8 +33,7 @@ gsk_vulkan_buffer_new (GdkVulkanContext *context, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = size, .flags = 0, - .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT - | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + .usage = usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }, NULL, @@ -55,6 +55,22 @@ gsk_vulkan_buffer_new (GdkVulkanContext *context, return self; } +GskVulkanBuffer * +gsk_vulkan_buffer_new (GdkVulkanContext *context, + gsize size) +{ + return gsk_vulkan_buffer_new_internal (context, size, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); +} + +GskVulkanBuffer * +gsk_vulkan_buffer_new_staging (GdkVulkanContext *context, + gsize size) +{ + return gsk_vulkan_buffer_new_internal (context, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); +} + void gsk_vulkan_buffer_free (GskVulkanBuffer *self) { diff --git a/gsk/gskvulkanbufferprivate.h b/gsk/gskvulkanbufferprivate.h index e49719272e..30e327e10e 100644 --- a/gsk/gskvulkanbufferprivate.h +++ b/gsk/gskvulkanbufferprivate.h @@ -9,6 +9,8 @@ typedef struct _GskVulkanBuffer GskVulkanBuffer; GskVulkanBuffer * gsk_vulkan_buffer_new (GdkVulkanContext *context, gsize size); +GskVulkanBuffer * gsk_vulkan_buffer_new_staging (GdkVulkanContext *context, + gsize size); void gsk_vulkan_buffer_free (GskVulkanBuffer *buffer); VkBuffer gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self); diff --git a/gsk/gskvulkanimage.c b/gsk/gskvulkanimage.c index b82398d276..4b4b381010 100644 --- a/gsk/gskvulkanimage.c +++ b/gsk/gskvulkanimage.c @@ -1,6 +1,8 @@ #include "config.h" #include "gskvulkanimageprivate.h" + +#include "gskvulkanbufferprivate.h" #include "gskvulkanmemoryprivate.h" #include "gskvulkanpipelineprivate.h" @@ -136,6 +138,125 @@ gsk_vulkan_image_ensure_view (GskVulkanImage *self, &self->vk_image_view); } +GskVulkanImage * +gsk_vulkan_image_new_from_data_via_staging_buffer (GdkVulkanContext *context, + VkCommandBuffer command_buffer, + guchar *data, + gsize width, + gsize height, + gsize stride) +{ + GskVulkanImage *self; + GskVulkanBuffer *staging; + guchar *mem; + + staging = gsk_vulkan_buffer_new_staging (context, width * height * 4); + mem = gsk_vulkan_buffer_map (staging); + + if (stride == width * 4) + { + memcpy (mem, data, stride * height); + } + else + { + for (gsize i = 0; i < height; i++) + { + memcpy (mem + i * width * 4, data + i * stride, width * 4); + } + } + + gsk_vulkan_buffer_unmap (staging); + + self = gsk_vulkan_image_new (context, + width, + height, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + vkCmdPipelineBarrier (command_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, + 0, NULL, + 0, NULL, + 1, (VkImageMemoryBarrier[1]) { + { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_HOST_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = self->vk_image, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + } + }); + + vkCmdCopyBufferToImage (command_buffer, + gsk_vulkan_buffer_get_buffer (staging), + self->vk_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + (VkBufferImageCopy[1]) { + { + .bufferOffset = 0, + .imageSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1 + }, + .imageOffset = { 0, 0, 0 }, + .imageExtent = { + .width = width, + .height = height, + .depth = 1 + } + } + }); + + vkCmdPipelineBarrier (command_buffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, + 0, NULL, + 0, NULL, + 1, (VkImageMemoryBarrier[1]) { + { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = self->vk_image, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + } + }); + + /* XXX: Is this okay or do we need to keep the staging image around until the commands execute */ + gsk_vulkan_buffer_free (staging); + + gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_SRGB); + + return self; +} + GskVulkanImage * gsk_vulkan_image_new_from_data_via_staging_image (GdkVulkanContext *context, VkCommandBuffer command_buffer, @@ -325,6 +446,8 @@ gsk_vulkan_image_new_from_data (GdkVulkanContext *context, gsize height, gsize stride) { + if (GSK_RENDER_MODE_CHECK (STAGING_BUFFER)) + return gsk_vulkan_image_new_from_data_via_staging_buffer (context, command_buffer, data, width, height, stride); if (GSK_RENDER_MODE_CHECK (STAGING_IMAGE)) return gsk_vulkan_image_new_from_data_via_staging_image (context, command_buffer, data, width, height, stride); else -- 2.30.2